面向NDK开发者的Android变更
受 Android 平台其他改进的影响,为了方便加载本机代码,Android M 和 N 中的动态链接器对编写整洁且跨平台兼容的本机代码提出了更严格的要求。为了确保平滑过渡到近期发布的 Android 版本,应用的本机代码必须遵循这些规则和建议。
我们在下面详细说明了与加载本机代码有关的每一项变更及其影响,以及您可以采取哪些措施来避免出现问题。
所需工具:在 NDK 中,每一个架构都有一个 <arch>-linux-android-readelf 二进制文件(例如 arm-linux-androideabi-readelf 或 i686-linux-android-readelf)(位于 toolchains/ 下),但您可以将 readelf 用于任何架构,因为我们只进行基本检查。在 Linux 上,您需要为 readelf 安装 binutils 程序包,为 scanelf 安装 pax-utils 程序包。
在更新期间用户应获得一致的应用体验,开发者应不需要进行紧急应用更新来处理平台变更。因此,我们不推荐使用私有 C/C++ 符号。所有 Android 设备都必须通过的兼容性测试套件 (CTS) 不包含对私有符号进行的测试。它们可能不存在,或可能行为方式不同。因此,使用私有符号的应用很可能在某些设备上或在将来发布的版本中无法使用,在 Android 6.0 Marshmallow 从 OpenSSL 切换到 BoringSSL 时,很多开发者就发现了这种问题。
为了减少过渡期间对用户的影响,我们确定了在 Google Play 上安装最多的应用中颇为常用且我们在短期内仍可提供支持的一些库(包括 libandroid_runtime.so、libcutils.so、libcrypto.so 和 libssl.so)。为了给您更多的时间进行过渡,我们将暂时支持这些库;所以,如果您看到警告提示,这就意味着您的代码在将来发布的版本中将无法使用,请立即对其进行修复!
潜在问题:从 API 24 开始,动态链接器将不再加载私有库,并会阻止应用加载。
解决方案:重新编写本机代码,以便仅依赖公共 API。作为短期的变通方案,可以将没有复杂依赖关系的平台库 (libcutils.so) 复制到项目。作为长期的解决方案,必须将相关代码复制到项目树。SSL/Media/JNI 内部/binder API 不可以从本机代码访问。在必要时,本机代码应调用合适的公共 Java API 方法。
注:SSL/crypto 是特例,应用不得直接使用平台 libcrypto 和 libssl 库,即使在较早版本的平台上也不可以。所有应用都应使用 GMS 安全提供程序,以确保应用免遭已知漏洞攻击。
解决方案:在您的版本中取消删除节标题的步骤。
文本重定位的常见原因是使用了非定位独立手写汇编程序。这种情况并不常见。使用我们的文档中所述的 scanelf 工具进行进一步诊断:
如果您没有可用的 scanelf 工具,您可以使用 readelf 进行基本检查,查找 TEXTREL 条目或 TEXTREL 标志。查找其中之一便已足够。(对应于 TEXTREL 条目的值无关紧要,且其通常为 0,存在 TEXTREL 条目即可表明 .so 包含文本重定位)。本例同时存在两个指标:
潜在问题:重定位强制使代码页可写入,增加了内存中的脏页数量,因此非常浪费内存。从 Android K (API 19) 开始,动态链接器发布了有关文本重定位的警告,但在 API 23 及更高版本中,其拒绝加载带有文本重定位的代码。
解决方案:重新写入与位置无关的汇编程序,确保不需要文本重定位。查看 Gentoo 文档,了解相关详细信息:
wiki.gentoo.org/wiki/Hardened/Textrels_Guide
在 API 23 之前,Android 的动态链接器在查找所需库时会忽略完整路径,仅使用基名(最后一个 ‘/’ 之后的部分)。从 API 23 开始,运行时链接器将完全服从 DT_NEEDED,所以,如果设备的特定位置不存在库,其将无法加载库。
更糟糕的是,有些编译系统存在漏洞,这会导致它们插入指向构建主机上的文件的 DT_NEEDED 条目,而在设备上将无法找到这种文件。
解决方案:确保所有所需的库仅通过 SONAME 引用。最好让运行时链接器查找和加载这些库,因为库的位置可能在不同的设备上有所不同。
每个 ELF 共享对象(“原生库”)必须有一个 SONAME(共享对象名称)属性。NDK 工具链会默认添加此属性,若无此属性,则表明备用工具链或编译系统存在配置错误。缺少 SONAME 可能导致运行时问题,例如加载错误的库:缺少此属性时将使用文件名。
解决方案:当前 NDK 默认情况下生成正确的 SONAME。确保使用最新版 NDK,且未将编译系统配置为生成不正确的 SONAME 条目(使用 -soname 链接器选项)。
请记住,使用最新版 NDK 构建的整洁的跨平台代码在 Android N 上应没有问题。我们建议您修改本机代码的构建,以便生成正确的二进制文件。
查看文中所有链接,请点击文末阅读原文。
7.26 Google Doodle: Maria Severa Onofriana 诞辰196周年